﻿using System;
using System.Collections.Generic;
using System.Collections;

namespace BitTorrent
{
    public class Bitfield : IEnumerable<bool>
    {
        private byte[] _bytes;
        private bool _ownBytes;

        public Bitfield() {
        }

        public Bitfield(int bits) {
            Resize(bits);
        }

        public Bitfield(byte[] bytes) {
            _bytes = bytes;
            Size = _bytes.Length * 8;
        }

        public int Size { get; private set; }

        public byte[] Bytes { get { return _bytes; } }

        public void Resize(int bits) {
            int bytes = (bits + 7) / 8;

            if (_bytes == null) {
                _bytes = new byte[bytes];
                _ownBytes = true;
            } else {
                if (_ownBytes) {
                    Array.Resize(ref _bytes, bytes);
                    _ownBytes = true;
                } else if (bits > Size) {
                    Array.Resize(ref _bytes, bytes);
                    _ownBytes = true;
                }
            }
            Size = bits;
        }

        public void SetAll() {
            for (int i = 0; i < _bytes.Length; i++) {
                _bytes[i] = 0xff;
            }
        }

        public void ClearAll() {
            Array.Clear(_bytes, 0, _bytes.Length);
        }

        public bool GetBit(int index) {
            if (index < 0 || index > Size)
                throw new ArgumentOutOfRangeException("index");

            return (_bytes[index / 8] & (0x80 >> (index & 7))) == 0x00 ? false : true;
        }

        public void SetBit(int index) {
            if (index < 0 || index > Size)
                throw new ArgumentOutOfRangeException("index");

            _bytes[index / 8] |= (byte)((0x80 >> (index & 7)));
        }

        public void ClearBit(int index) {
            if (index < 0 || index > Size)
                throw new ArgumentOutOfRangeException("index");

            _bytes[index / 8] &= (byte)(~(0x80 >> (index & 7)));
        }

        public bool this[int index] {
            get {
                return GetBit(index);
            }
            set {
                if (value) {
                    SetBit(index);
                } else {
                    ClearBit(index);
                }
            }
        }

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>1</filterpriority>
        public IEnumerator<bool> GetEnumerator() {
            throw new System.NotImplementedException();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }
    }
}
